function done(ctx, body, code = 200) {
    ctx.status = code;
    if (body !== undefined &&
        body !== null) {
        ctx.body = body;
    } else {
        ctx.status = 204;
    }
}

export function fail(ctx, err) {
    let status = 500;
    let message = '';
    switch (typeof err) {
    case 'number':
        status = err;
        break;
    case 'string':
        message = err;
        break;
    case 'object':
        switch (typeof err.code) {
        case 'number':
            status = err.code;
            break;
        case 'string':
            if (err.code.startsWith('LIMIT_')) {
                status = 413; // for multer
            }
            break;
        default:
            if (typeof err.statusCode === 'number') {
                status = err.statusCode;
            } else if (typeof err.status === 'number') {
                status = err.status;
            }
            break;
        }
        message = err.message || err.text || err.toString();
        break;
    }
    ctx.status = status;
    ctx.body = message;
    return {status, message};
}

/**
 * 为ctx添加done和fail方法
 */
export function ext() {
    return async (ctx, next) => {
        ctx.done = done.bind(undefined, ctx);
        ctx.fail = fail.bind(undefined, ctx);
        await next();
    };
}

/**
 * 通用的错误处理中间件
 * @param opts 选项（log - 日志）
 */
/* eslint no-console: 0 */
export function error(opts) {
    const log = opts.log || console.error.bind(console);

    return (ctx, next) => next().catch(err => {
        const {status, message} = fail(ctx, err);
        log.error(`${ctx.method} ${ctx.path} [${status}] ${message}`);
        if (process.env.NODE_ENV !== 'production') console.error(err);
    });
}
